/*
 * @Author: DONGDONG
 * @Date: 2022-01-19 21:12:32
 * @LastEditTime: 2022-01-19 21:40:31
 * @LastEditors: DONGDONG
 * @FilePath: \lagoufed-e-task\part1\fed-e-task-01-01\code\MyPromise.js
 */
/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.
*/

const PENDING = 'pending' // 等待
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'rejected' // 失败

class MyPromise {
	constructor(executor) {
		try {
			executor(this.resolve, this.reject)
		} catch (e) {
			this.reject(e)
		}
	}

	// promise状态
	status = PENDING
	value = undefined
	reason = undefined

	// 成功回调，多次使用then函数，多次记录
	successCallback = []
	// 失败回调
	failCallback = []

	resolve = (value) => {
		if (this.status !== PENDING) return
		this.status = FULFILLED
		this.value = value
		// 判断成功回调是否存在 如果存在 调用
		while (this.successCallback.length) {
			// 从最先进入数组的回调函数开始调用
			this.successCallback.shift()()
		}
	}

	reject = (reason) => {
		if (this.status !== PENDING) return
		this.status = REJECTED
		this.reason = reason
		// 判断失败回调是否存在 如果存在 调用
		while (this.failCallback.length) {
			// 从最先进入数组的回调函数开始调用
			this.failCallback.shift()()
		}
	}

	then(successCallback, failCallback) {
		successCallback = successCallback ? successCallback : (value) => value
		failCallback = failCallback
			? failCallback
			: (reason) => {
					throw reason
			  }
		let promise2 = new MyPromise((resolve, reject) => {
			if (this.status === FULFILLED) {
				setTimeout(() => {
					try {
						let x = successCallback(this.value)
						// 判断 x 的值是普通值还是promise对象
						// 如果是普通值，直接调用 resolve
						// 如果是promise对象，查看promise对象返回的结果
						// 再根据promise对象返回的结果，决定调用resolve还是调用reject
						resolvePromise(promise2, x, resolve, reject)
					} catch (e) {
						reject(e)
					}
				}, 0)
			} else if (this.status === REJECTED) {
				setTimeout(() => {
					try {
						let x = failCallback(this.reason)
						// 判断 x 的值是普通值还是promise对象
						// 如果是普通值，直接调用 resolve
						// 如果是promise对象，查看promise对象返回的结果
						// 再根据promise对象返回的结果，决定调用resolve还是调用reject
						resolvePromise(promise2, x, resolve, reject)
					} catch (e) {
						reject(e)
					}
				}, 0)
			} else {
				// 等待
				this.successCallback.push(() => {
					setTimeout(() => {
						try {
							let x = successCallback(this.value)
							// 判断 x 的值是普通值还是promise对象
							// 如果是普通值，直接调用 resolve
							// 如果是promise对象，查看promise对象返回的结果
							// 再根据promise对象返回的结果，决定调用resolve还是调用reject
							resolvePromise(promise2, x, resolve, reject)
						} catch (e) {
							reject(e)
						}
					}, 0)
				})
				this.failCallback.push(() => {
					setTimeout(() => {
						try {
							let x = failCallback(this.reason)
							// 判断 x 的值是普通值还是promise对象
							// 如果是普通值，直接调用 resolve
							// 如果是promise对象，查看promise对象返回的结果
							// 再根据promise对象返回的结果，决定调用resolve还是调用reject
							resolvePromise(promise2, x, resolve, reject)
						} catch (e) {
							reject(e)
						}
					}, 0)
				})
			}
		})
		return promise2
	}

	finally(callback) {
		this.then(
			(value) => {
				return MyPromise.resolve(callback()).then(() => value)
			},
			(reason) => {
				return MyPromise.resolve(callback()).then(() => {
					throw reason
				})
			}
		)
	}

	catch(failCallback) {
		return this.then(undefined, failCallback)
	}

	static all(array) {
		let result = []
		let index = 0
		return new MyPromise((resolve, reject) => {
			function addData(i, value) {
				result[i] = value
				index++
				if (index === array.length) resolve(result)
			}
			for (let i = 0; i < array.length; i++) {
				const current = array[i]
				if (current instanceof MyPromise) {
					// promise对象
					current.then(
						(value) => addData(i, value),
						(reason) => reject(reason)
					)
				} else {
					// 普通值
					addData(i, array[i])
				}
			}
		})
	}

	static resolve(value) {
		if (value instanceof MyPromise) return value
		return new MyPromise((resolve) => resolve(value))
	}
}

function resolvePromise(promise2, x, resolve, reject) {
	if (promise2 === x) {
		// 不能返回自身promise对象，否则报错
		return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
	}
	if (x instanceof MyPromise) {
		// promise 对象
		x.then(resolve, reject)
	} else {
		// 普通值
		resolve(x)
	}
}

module.exports = MyPromise
